﻿using System;
using System.Diagnostics.Contracts;

namespace Postnuclear
  {
  /// <summary>
  /// Provides some value and notifies when it's changed.
  /// </summary>
  /// <typeparam name="TValue">The type of the value.</typeparam>
  internal sealed class ValueProvider<TValue>
    : IValueProvider<TValue>
    {
    #region Public

    // Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="ValueProvider{TValue}"/> class.
    /// </summary>
    /// <param name="observer">The value observer.</param>
    /// <param name="valueEvaluator">The value evaluator.</param>
    public ValueProvider(INotifyValueChanged observer, Func<TValue> valueEvaluator)
      {
      Contract.Requires<ArgumentNullException>(observer != null);
      Contract.Requires<ArgumentNullException>(valueEvaluator != null);

      Observer = new EventObserver<EventArgs>(
        handler =>
          {
          observer.ValueChanged += handler;
          Value = valueEvaluator();
          },
        handler => observer.ValueChanged -= handler,
        (sender, e) => Value = valueEvaluator());
      Value = valueEvaluator();
      }

    // Events

    /// <summary>
    /// Occurs when the <see cref="Value"/> of the instance is changed.
    /// </summary>
    public event EventHandler<EventArgs> ValueChanged
      {
      add
        {
        Observer.Event += value;
        }

      remove
        {
        Observer.Event -= value;
        }
      }

    // Properties

    /// <summary>
    /// Gets the source expression value.
    /// </summary>
    public TValue Value
      {
      get;
      private set;
      }

    #endregion

    #region Private

    // Fields

    private readonly EventObserver<EventArgs> Observer;

    #endregion
    }
  }
